#include "VulkanApplication.h"
#include <vector>
#include <iostream>

int main(int argc, char** argv)
{
    VulkanApplication app("c02_devices");
    if (!app.initialized())
    {
        printf("Failed to initialize VulkanApplication.\n");
        return 1;
    }

    // Get layers
    uint32_t layerCount = 0;
    vkEnumerateInstanceLayerProperties(&layerCount, nullptr);

    std::vector<VkLayerProperties> layers(layerCount);
    vkEnumerateInstanceLayerProperties(&layerCount, layers.data());
    printf("vkEnumerateInstanceLayerProperties() has found some layers:\n");
    for (uint32_t i = 0; i < layerCount; ++i)
    {
        printf("    %d: %s (%s)\n", i, layers[i].layerName, layers[i].description);
    }

    // Find physical device
    uint32_t deviceCount = 0;
    VkResult result = vkEnumeratePhysicalDevices(app.getInstance(), &deviceCount, nullptr);
    if (result != VK_SUCCESS || deviceCount == 0)
    {
        printf("Failed to enumerate physical devices.\n");
        return 1;
    }

    std::vector<VkPhysicalDevice> devices(deviceCount);
    vkEnumeratePhysicalDevices(app.getInstance(), &deviceCount, devices.data());
    printf("vkEnumeratePhysicalDevices() has found some physical devices:\n");
    for (uint32_t i = 0; i < deviceCount; ++i)
    {
        VkPhysicalDeviceProperties props;
        vkGetPhysicalDeviceProperties(devices[i], &props);

        if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
            printf("    %d: Discrete GPU, %s\n", i, props.deviceName);
        else if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
            printf("    %d: Integrated GPU, %s\n", i, props.deviceName);
        else
            printf("    %d: Other GPU, %s\n", i, props.deviceName);
    }

    uint32_t queueFamilyCount = 0;
    vkGetPhysicalDeviceQueueFamilyProperties(devices[0], &queueFamilyCount, nullptr);
    if (queueFamilyCount == 0)
    {
        printf("Failed to get queue family data.\n");
        return false;
    }

    std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
    vkGetPhysicalDeviceQueueFamilyProperties(devices[0], &queueFamilyCount, queueFamilies.data());

    VkPhysicalDeviceFeatures features;
    vkGetPhysicalDeviceFeatures(devices[0], &features);
    
    printf("Current physical device supports:\n");
    printf("    QueueFlags = %x\n", queueFamilies[0].queueFlags);
    printf("    Graphics = %d\n", (queueFamilies[0].queueFlags & VK_QUEUE_GRAPHICS_BIT) > 0);
    printf("    Compute = %d\n", (queueFamilies[0].queueFlags & VK_QUEUE_COMPUTE_BIT) > 0);
    printf("    Geometry = %d\n", features.geometryShader);
    printf("    Tessellation = %d\n", features.tessellationShader);

    // Create logical device
    float queuePriorities = 1.0f;
    VkDeviceQueueCreateInfo queueCreateInfo
    {
        VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
        nullptr, 0,
        0, 1,  // queueFamilyIndex, queueCount
        &queuePriorities
    };

    VkDeviceCreateInfo createInfo
    {
        VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
        nullptr, 0,
        1, &queueCreateInfo,
        0, nullptr,
        0, nullptr,
        &features
    };

    VkDevice logicDevice = nullptr;
    result = vkCreateDevice(devices[0], &createInfo, nullptr, &logicDevice);
    if (result != VK_SUCCESS)
    {
        printf("Failed to create logic device.\n");
        return 1;
    }

    printf("Logic device created successfully.\n");
    // TODO

    vkDestroyDevice(logicDevice, nullptr);
    return 0;
}